#include <iostream>
#include "calculator.h"
#include "util/argument_parser.h"

using namespace std;
using namespace tbox::util;

int main(int argc, const char **argv) {
    Calculator::Env e;
    Calculator::Bullet b;
    double speed = 0;

    Calculator::Target t;
    int times = 0;

    double fly_angle = 0;
    double fly_length = 0;

    bool print_usage = false;

    ArgumentParser parser(
        [&](char short_opt, const string &long_opt, ArgumentParser::OptionValue& opt_value) {
            try {
                if (long_opt == "e.G") {
                    e.G = stod(opt_value.get());
                } else if (long_opt == "e.T") {
                    e.T = stod(opt_value.get());
                } else if (long_opt == "e.AP") {
                    e.AP = stod(opt_value.get());
                } else if (long_opt == "b.m") {
                    b.m = stod(opt_value.get()) / 1000;
                } else if (long_opt == "b.d") {
                    b.d = stod(opt_value.get()) / 1000;
                } else if (long_opt == "b.Cd") {
                    b.Cd = stod(opt_value.get());
                } else if (long_opt == "speed") {
                    speed = stod(opt_value.get());
                } else if (long_opt == "t.d") {
                    t.distance = stod(opt_value.get());
                } else if (long_opt == "t.a") {
                    t.angle = stod(opt_value.get());
                } else if (long_opt == "times") {
                    times = stoi(opt_value.get());
                } else if (long_opt == "f.a") {
                    fly_angle = stof(opt_value.get());
                } else if (long_opt == "f.l") {
                    fly_length = stof(opt_value.get());
                } else if (short_opt == 'h' || long_opt == "help") {
                    print_usage = true;
                } else {
                    print_usage = true;
                    if (short_opt != 0)
                        cerr << "Error: invalid option `-" << short_opt << "'" << endl;
                    else
                        cerr << "Error: invalid option `--" << long_opt << "'" << endl;
                    return false;
                }

            } catch (const exception &e) {
                print_usage = true;
                cout << "Got exception: " << e.what() << endl;
                return false;
            }

            return true;
        }
    );

    if (parser.parse(argc, argv)) {
        //! 检查输入的数据是否合法
        if (b.m <= 0.0) {
            cout << "Error: b.m is required" << endl;
            print_usage = true;
        }

        if (b.d <= 0.0) {
            cout << "Error: b.d is required" << endl;
            print_usage = true;
        }

        if (speed <= 0.0) {
            cout << "Error: speed is required" << endl;
            print_usage = true;
        }

    } else {
        cout << "Error: parameter invalid!" << endl;
        print_usage = true;
    }

    if (print_usage) {
        cout \
            << endl
            << "This is ballistic calculator, designed by Hevake. v2" << endl << endl
            << "Usage: " << argv[0] << " OPTIONS" << endl
            << endl <<
            R"(OPTIONS:

  --b.m     ball's mass, unit:g, required
  --b.d     ball's diameter, unit:mm, required
  --speed   b.s speed, unit:m/s, required
  --e.G     gravitational acceleration, unit:m/s2, default: 9.81
  --e.T     temperature, default: 20
  --e.AP    air press, default: 1
  --b.Cd    ball's C_D, default: 0.47

  --t.d     target's distance, unit:m, required
  --t.a     target's angle, unit:degree, default: 0
  --times   calculate times, default 10000

  --f.a     fly angle
  --f.l     calculate plice length

EXAMPLE:
  )" << argv[0] << " --b.m 2.088 --b.d 8 --speed 180 --t.d 50"
            << endl << endl;
        return 0;
    }

    cout << "=========================" << endl;
    ::printf("e.G    : %.3f m/s2\n", e.G);
    ::printf("e.T    : %.1f \n", e.T);
    ::printf("e.AP   : %.1f \n", e.AP);
    ::printf("b.m    : %.3f g\n", b.m * 1000);
    ::printf("b.d    : %.1f mm\n", b.d * 1000);
    ::printf("b.Cd   : %.3f\n", b.Cd);
    ::printf("speed  : %.2f m/s\n", speed);
    ::printf("t.d    : %.2f m\n", t.distance);
    ::printf("t.a    : %.2f 'C\n", t.angle);
    ::printf("times  : %d 'C\n", times);
    ::printf("f.l    : %.2f m\n", fly_length);
    ::printf("f.a    : %.2f m\n", fly_angle);
    cout << "-------------------------" << endl;

    Calculator c;

    c.setEnv(e);
    c.setBullet(b);
    c.setSpeed(speed);

    if (t.distance > 0) {
        c.setTarget(t);

        if (times > 0)
            c.setTimes(times);

        Calculator::AngleResult result;

        bool is_succ = c.findBestFlyAngle(result);

        ::printf("start_E  : %.3f J\n", result.start_E);
        ::printf("start_EpA: %.3f J/cm2\n", result.start_EpA);

        cout << "-------------------------" << endl;

        ::printf("is_succ: %s\n", is_succ ? "yes" : "no");
        ::printf("angle  : %.4f 'C\n", result.start_angle);
        ::printf("r_angle: %.4f 'C\n", result.relative_angle);
        ::printf("r_mil  : %.3f mil\n", result.relative_mil);
        ::printf("tof    : %.3f sec\n", result.tof);
        ::printf("angle2 : %.4f 'C\n", result.final_angle);
        ::printf("v      : %.3f m/s\n", result.v);
        ::printf("v_h    : %.3f m/s\n", result.v_h);
        ::printf("v_v    : %.3f m/s\n", result.v_v);
        ::printf("E      : %.3f J\n", result.E);
        ::printf("EpA    : %.3f J/cm2\n", result.EpA);
        ::printf("percent: %.3f %%\n", result.percent);
        ::printf("error  : %.3f m\n", result.error);
        ::printf("max_distance: %.2f m\n", result.max_h_distance);

        cout << "=========================" << endl;
    }

    if (fly_length > 0) {
        c.fly(fly_angle, fly_length);
    }

    return 0;
}
